#include "Phong.h"



Color Phong::illumination(Intersection& inter, const Object& object, Scene& scene, Ray& ray, int compteur){

	Intersection lightCut = Intersection();
	Color I_a_lambda, O_d_lambda, I_p_lambda_i, O_s_lambda;
	Color I_lambda;
	Vector N, V, L_i, H_i;
	double K_a, F_atti, K_d, K_s, n, N_L_i, N_H_i;

	float tmpR, tmpG, tmpB;
	bool ombre; 
	double cpt, mirorReflexions=0;
	Point pTemp;
	bool intersection;

	vector<Light *> lights = scene.getLights();
	vector<Light *>::iterator il;
	vector<Object *> objects = scene.getObjects();
	vector<Object *>::iterator io;
	
	N = inter.getNormal();

	if(object.getBumpMapping() != 0)
	{
		float noiseCoefx = float(BumpMapping().noise(object.getBumpMapping() * double(inter.getP().get_x()), object.getBumpMapping() * double(inter.getP().get_y()),object.getBumpMapping() * double(inter.getP().get_z())));
		float noiseCoefy = float(BumpMapping().noise(object.getBumpMapping() * double(inter.getP().get_y()), object.getBumpMapping() * double(inter.getP().get_z()),object.getBumpMapping() * double(inter.getP().get_x())));
		float noiseCoefz = float(BumpMapping().noise(object.getBumpMapping() * double(inter.getP().get_z()), object.getBumpMapping() * double(inter.getP().get_x()),object.getBumpMapping() * double(inter.getP().get_y())));
	   
		N.set_x(inter.getNormal().get_x() + noiseCoefx); 
		N.set_y(inter.getNormal().get_y() + noiseCoefy); 
		N.set_z(inter.getNormal().get_z() + noiseCoefz); 
		
		double temp = N.scalar(N);
		if (temp == 0.0f)
			N = inter.getNormal();
		temp = 1/sqrt(temp);
		
		N = N * temp;
		N.normalize();
	}
	
	V = Vector(inter.getP(), scene.getObserver().getLocation());
	V.normalize();
	O_d_lambda = object.getColor();
	O_s_lambda = object.getSpecular();
	K_d = object.getDiffusionCoefficient();	
	K_s = object.getSpecularCoefficient();
	n = object.getRugositeCoefficient();
	//ambiante
	I_a_lambda = scene.getAmbient();
	K_a = AMBIENT;
	if(compteur!=0) mirorReflexions = object.getMirorReflexion();
	tmpR = (1 - mirorReflexions) * I_a_lambda.getR() * K_a * O_d_lambda.getR();

	tmpG = (1 - mirorReflexions) * I_a_lambda.getG() * K_a * O_d_lambda.getG();
	tmpB = (1 - mirorReflexions) * I_a_lambda.getB() * K_a * O_d_lambda.getB();

	for(il = lights.begin(); il != lights.end(); il++) {
		F_atti = attenuation(inter.getP(), (*il)->getPoint());
		I_p_lambda_i = (*il)->getColor();
		cpt=NB_LIGHT;
		for(int i=INIT_BOUCLE; i<FIN_BOUCLE; i++){
			for(int j=INIT_BOUCLE; j<FIN_BOUCLE; j++){
				for(int k=INIT_BOUCLE; k<FIN_BOUCLE; k++){
					pTemp = Point ((*il)->getPoint());
					pTemp.set_x(pTemp.get_x()+i*0.1);
					pTemp.set_y(pTemp.get_y()+j*0.1);
					pTemp.set_z(pTemp.get_z()+k*0.1);
					L_i = Vector(inter.getP(), pTemp);
					L_i.normalize();
					H_i = L_i + V;
					H_i.normalize();

					N_L_i = N.scalar(L_i);
					N_H_i = N.scalar(H_i);
					Ray lightRay = Ray(inter.getP(),L_i);
					ombre = false;

					if (N_H_i > 0 && N_L_i > 0) {
						for(io = objects.begin(); io != objects.end(); io++)
						{
							intersection=(*io)->intersection(lightRay, lightCut);
							if(intersection)
							{
								if( lightCut.getDistance() < inter.getP().distance((*il)->getPoint()))
								{
									ombre = true;
								}
							}
						}
					}
					if(ombre==true) cpt--;
				}
			}
		}
		
		//miroir
		if((mirorReflexions>0) && (compteur !=0))
		{
			miror(inter, object, scene, ray, compteur, I_lambda);
			tmpR += I_lambda.getR()*mirorReflexions;
			tmpG += I_lambda.getG()*mirorReflexions;
			tmpB += I_lambda.getB()*mirorReflexions;
		}

		if (N_H_i > 0 && N_L_i > 0) {
			
			if(object.existTexture()!=NULL)
			{
				Point p = inter.getP();
				Color c = object.getTexture().getColorMappingPlanaire(p);
				tmpR += (1 - mirorReflexions) * (cpt/NB_LIGHT) * F_atti * I_p_lambda_i.getR() * ( K_d * c.getR() * N_L_i + O_s_lambda.getR() * K_s * pow(N_H_i, n));
    			tmpG += (1 - mirorReflexions) * (cpt/NB_LIGHT) * F_atti * I_p_lambda_i.getG() * ( K_d * c.getG() * N_L_i + O_s_lambda.getG() * K_s * pow(N_H_i, n));
    			tmpB += (1 - mirorReflexions) * (cpt/NB_LIGHT) * F_atti * I_p_lambda_i.getB() * ( K_d * c.getB() * N_L_i + O_s_lambda.getB() * K_s * pow(N_H_i, n));
			}
			else
			{
    			tmpR += (1 - mirorReflexions) * (cpt/NB_LIGHT) * F_atti * I_p_lambda_i.getR() * ( K_d * O_d_lambda.getR() * N_L_i + O_s_lambda.getR() * K_s * pow(N_H_i, n));
    			tmpG += (1 - mirorReflexions) * (cpt/NB_LIGHT) * F_atti * I_p_lambda_i.getG() * ( K_d * O_d_lambda.getG() * N_L_i + O_s_lambda.getG() * K_s * pow(N_H_i, n));
    			tmpB += (1 - mirorReflexions) * (cpt/NB_LIGHT) * F_atti * I_p_lambda_i.getB() * ( K_d * O_d_lambda.getB() * N_L_i + O_s_lambda.getB() * K_s * pow(N_H_i, n));
				}
		}
		if(tmpR>1) tmpR=1;
		if(tmpG>1) tmpG=1;
		if(tmpB>1) tmpB=1;
	}
	return Color (tmpR, tmpG, tmpB);
}

float Phong::attenuation(const Point& p1,const Point& p2)
{
 	/*Vector* tmp = new Vector (p1,p2);
 	double norm = tmp->norm();
 	if(1 - 0.03 * norm > 0)
 		return 1 - 0.01 * norm;
 	else
 		return 0;*/
   return 1;
}

void Phong::miror( Intersection& interPar, const Object& object, Scene& scene, Ray& ray, int compteur, Color& c)
{	
	compteur--;
	Vector v = ray.getDirection();
	v.inverse();

	Vector res;
	res = interPar.getNormal() * 2 * ((v.scalar(interPar.getNormal()) )/( pow(v.scalar(v),0.5) * pow(interPar.getNormal().scalar(interPar.getNormal()),0.5)))- v;
	res.normalize();
	Ray rGlobal= Ray(interPar.getP(), res);
	
	vector<Object *>::iterator id;
	Intersection inter;
	Phong illum = Phong();
	Intersection bestInter = Intersection();
	Color col;
	bool intersection, first=true;

	vector<Object *> objects = scene.getObjects();
	for(id = objects.begin(); id != objects.end(); id++)
	{	
		if( object.getId() != (*id)->getId())
		{
			intersection=(*id)->intersection(rGlobal, interPar);
			
			if(intersection && !(inter.getP()== interPar.getP()) )
			{
				if(!first && inter.getDistance() < bestInter.getDistance())
				{
					bestInter=inter;
					col = illum.illumination(bestInter,(**id), scene, rGlobal, compteur);
				}
				if(first)
				{	
					first = false;
					bestInter=inter;
					col = illum.illumination(bestInter,(**id), scene, rGlobal, compteur);
				}
			}
		}
	}
	if(first)
	{
		c = scene.getBackground();
	}
	else
	{	
		c = col;
	}
}

void Phong::glossy( Intersection& interPar, const Object& object, Scene& scene, Ray& ray, int compteur, Color& c)
{	
	compteur--;
	Vector v = ray.getDirection();
	v.inverse();
	Color colorTemp = Color();
	
	double nombre;

	Vector res;
	for (int i=0; i<GLOSSY_NB_RAYON ; i++)
	{
		res = interPar.getNormal() * 2 * ((v.scalar(interPar.getNormal()) )/( pow(v.scalar(v),0.5) * pow(interPar.getNormal().scalar(interPar.getNormal()),0.5)))- v;
		nombre = ((((double)rand()) / ((double)RAND_MAX + 1))*2)-1;
		res.set_x(res.get_x()+nombre*glosyCoef);
		nombre = ((((double)rand()) / ((double)RAND_MAX + 1))*2)-1;
		res.set_y(res.get_y()+nombre*glosyCoef);
		nombre = ((((double)rand()) / ((double)RAND_MAX + 1))*2)-1;
		res.set_z(res.get_z()+nombre*glosyCoef);
		res.normalize();

		Ray rGlobal= Ray(interPar.getP(), res);

		vector<Object *>::iterator id;
		Intersection inter;
		Phong illum = Phong();
		Intersection bestInter = Intersection();
		Color col;
		bool intersection, first=true;

		vector<Object *> objects = scene.getObjects();
		for(id = objects.begin(); id != objects.end(); id++)
		{	
			intersection=(*id)->intersection(rGlobal, inter);
			
			if(intersection && !(inter.getP()== interPar.getP()) )
			{
				if(!first && inter.getDistance() < bestInter.getDistance())
				{
					bestInter=inter;
					col = illum.illumination(bestInter,(**id), scene, rGlobal, compteur);
				}
				if(first)
				{	
					first = false;
					bestInter=inter;
					col = illum.illumination(bestInter,(**id), scene, rGlobal, compteur);
				}
			}
		}
		if(first)
		{
			colorTemp += scene.getBackground();
		}
		else
		{	
			colorTemp += col;
		}
	}
	colorTemp.div(GLOSSY_NB_RAYON);
	c = colorTemp;
}

